
if(!window.Berta) window.Berta = {};
if(!Berta.Server) Berta.Server = {};

// uploader
Berta.Server.Upload = function(request, options){

	this.request = request;

	// options
	this.defaults = {
		callbacks: {
			onStart: undefined,
			onProgress: undefined,
			onComplete: undefined,
			onFileStart: undefined,
			onFileErorr: undefined,
			onFileComplete: undefined
		}
	}
	this.options = $.extend(true, {}, this.defaults, options);

	// don't touch the following
	
	this._ = function(callback) { var self = this; return function() { return callback.apply(self, arguments); }; };
	this.id = $.uniqid.generate();
	this.xhr = this._(initXHR)();

	this.isRunning = false;
	this.queue = [];
	this.bytesTotal = 0;
	this.bytesUploaded = 0;
	
	this.currentFile = null;
	this.currentBytesTotal = 0;
	this.currentBytesUploaded = 0;
	
	//   .______    __    __  .______    __       __    ______    ////////////////////////////////////////////////////////////
	//   |   _  \  |  |  |  | |   _  \  |  |     |  |  /      |   ////////////////////////////////////////////////////////////
	//   |  |_)  | |  |  |  | |  |_)  | |  |     |  | |  ,----'   ////////////////////////////////////////////////////////////
	//   |   ___/  |  |  |  | |   _  <  |  |     |  | |  |        ////////////////////////////////////////////////////////////
	//   |  |      |  `--'  | |  |_)  | |  `----.|  | |  `----.   ////////////////////////////////////////////////////////////
	//   | _|       \______/  |______/  |_______||__|  \______|   ////////////////////////////////////////////////////////////


	this.add = /* API */ function(fileOrFiles) {
		if(fileOrFiles instanceof FileList) {
			for(i = 0; i < fileOrFiles.length; i++) {
				this.queue.push(fileOrFiles[i]);
				//uploadNameQueue.push(fileOrFiles[i].fileName);
				this.bytesTotal += fileOrFiles[i].fileSize;
			}
		} else {
			this.queue.push(fileOrFiles);
			//uploadNameQueue.push(fileOrFiles.fileName);
			this.bytesTotal += fileOrFiles.fileSize;
		}
	};

	
	if(this.request.fileUpload) {
		this.add(this.request.container
					? this.request.data[this.request.container][this.request.fileDataKey]
					: this.request.data[this.request.fileDataKey]);
	}



	
	this.start = /* API */ function() {
		if (this.queue.length && !this.isRunning) {
			this._(onAllStart)();
			this._(upload)(this.currentFile = this.queue.shift());
		}
	};
	
	this.cancel = /* API */ function() {
		this.xhr.abort();
		this.isRunning = false;
		this.reset();
	};
	this.reset = /* API */ function() {
		this.queue = [];
		this.bytesTotal = 0;
		this.bytesUploaded = 0;
		this.currentFile = null;
		this.currentBytesTotal = 0;
		this.currentBytesUploaded = 0;
		this.currentFile = null;
	};
	
	
	this.destroy = /* API */ function() {
		this.xhr.onerror = null;
		this.xhr.onload = null;
		this.xhr.upload['onprogress'] = null;
		try {
			this.xhr.abort();
		} catch(e) { }
		
		this.xhr = null;
		this.request = null;
		this.options = null;
	};
	
	
	
	
	//   .______   .______     __  ____    ____  ___   .___________. _______    //////////////////////////////////////////////////
	//   |   _  \  |   _  \   |  | \   \  /   / /   \  |           ||   ____|   //////////////////////////////////////////////////
	//   |  |_)  | |  |_)  |  |  |  \   \/   / /  ^  \ `---|  |----`|  |__      //////////////////////////////////////////////////
	//   |   ___/  |      /   |  |   \      / /  /_\  \    |  |     |   __|     //////////////////////////////////////////////////
	//   |  |      |  |\  \--.|  |    \    / /  _____  \   |  |     |  |____    //////////////////////////////////////////////////
	//   | _|      | _| `.___||__|     \__/ /__/     \__\  |__|     |_______|   //////////////////////////////////////////////////
	
	
	function initXHR() {
		var xhr = new XMLHttpRequest();
		xhr.upload['onprogress'] = this._(onFileProgress);
		xhr.onerror = this._(onFileError);
		xhr.onload = this._(onFileComplete);
		return xhr;
	}
	
	function upload(file) {
		this._(onFileStart)(file);
		this.xhr.open("post", this.request.url, true);
		this.xhr.setRequestHeader("Cache-Control", "no-cache");

		var boundary = "Berta_Server_Upload_" + (new Date).getTime();
		this.xhr.setRequestHeader("Content-Type", "multipart/form-data; boundary=" + boundary);
		
		
		if (window.FormData) {
			// Chrome 5.0.375.55+, Safari 5+ and Firefox 4+
            $.log('Upload: upload file (with FormData): ', file);
			var f = new FormData();
			
			// add data...
			var data = get_parsed_data(this.request.data, this.request.container, true);
			for(var d in data) {
				if(data.hasOwnProperty(d)) {
					f.append(d, data[d]);
				}
			}

			// add file...
			f.append(this.request.container
						? (this.request.container + '[' + this.request.fileDataKey + ']')
						: this.request.fileDataKey,
					 file);
			
			// send..
			//$.log('FormData', f);
            this.xhr.send(f);
        
		} else if(this.xhr.sendAsBinary && file.getAsBinary) {
			// Firefox 3+
			$.log('Upload: upload file (sendAsBinary + getAsBinary): ', file);
			this.xhr.sendAsBinary(this._(getRFC2388)(boundary, file));
		
		} else {
			// TODO: yet to implement 
			//this.xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
			//this.xhr.setRequestHeader("X-File-Name", file.fileName);
			//this.xhr.setRequestHeader("X-File-Size", file.fileSize);
			//this.xhr.setRequestHeader("X-POST-Params", ...);
			//this.xhr.send(file);
			
			$.logError('Server.Upload: Unsupported browser. No file uploads :(');
			$.log('window.FormData = ', window.FormData);
			$.log('file.getAsBinary ', file.getAsBinary);
			$.log('this.xhr.sendAsBinary ', this.xhr.sendAsBinary);
		}
		
		
		
	}
	
	function getRFC2388(boundary, file){
       //$.log(file.getAsBinary());
	    var CRLF = "\r\n";
		var str = '';

		var data = get_parsed_data(this.request.data, this.request.container, true);
		for(var d in data) {
			if(data.hasOwnProperty(d)) {
				str += '--' + boundary + CRLF +
					'Content-Disposition: form-data; name="' + d + '"' + CRLF +
					CRLF +
					data[d] + CRLF;
			}
		}
		
		//v = this.options.containerVarName ? (this.options.containerVarName + '[' + this.options.varName + ']') : this.options.varName;
		var file_var_name = this.request.container
									? (this.request.container + '[' + this.request.fileDataKey + ']')
									: this.request.fileDataKey;
		str +=  "--".concat(
            boundary, CRLF,
            'Content-Disposition: form-data; name="', file_var_name, '"; filename="', file.fileName, '"', CRLF,
            "Content-Type: application/octet-stream", CRLF,
            CRLF,
            file.getAsBinary(), CRLF,
            "--", boundary, "--", CRLF
        );
		
		return str;
    }

	function get_parsed_data(data, containerName, excludeFile) {
		var d_out = {};

		for(var d in data) {
			if(data.hasOwnProperty(d)) {

				if(d === containerName) {
					for(var dd in data[d]) {
						if(data[d].hasOwnProperty(dd)) {
							if(!excludeFile || !(data[d][dd] instanceof File || data[d][dd] instanceof FileList)) {
								d_out[d + '[' + dd + ']'] = data[d][dd];
							}
						}
					}

				} else if(!excludeFile || !(data[d] instanceof File || data[d] instanceof FileList)) {
					d_out[d] = data[d];
				}
			}
		}

		return d_out;
	}







	function onFileStart(file) {
		//$.log('Upload: file / start /', file);
		this.currentBytesTotal = file.fileSize;
		this.currentBytesUploaded = 0;
		
		this.options.callbacks.onFileStart ? this.options.callbacks.onFileStart(file) : false;
	}
	function onFileProgress(rpe){
		//$.log('Upload: file / progress /', rpe);
		this.currentBytesUploaded = rpe.loaded;
		this._(onAllProgress)();
	}
	function onFileComplete(event){
		//$.log('Upload: file / complete /', event);
		
		// update totals
		this.bytesUploaded += this.currentFile.fileSize;
		
		!this.options.callbacks.onFileComplete || this.options.callbacks.onFileComplete(this.currentFile, new Berta.Server.Response(event.target));
		this._(onAllProgress)();
		
		// next file
		this.currentFile = this.queue.shift();
		if (this.currentFile) {
			this._(upload)(this.currentFile);
		} else {
			this._(onAllComplete)();
		}
	}
	function onFileError(e) {
		$.log('Upload: file / error /', e);
		this.options.callbacks.onFileError ? this.options.callbacks.onFileError(this.currentFile) : false;
	}
	
	
	function onAllStart() {
		//$.log('Upload: start! TOTAL BYTES: ', this.bytesTotal);
		!this.options.callbacks.onStart || this.options.callbacks.onStart(this.bytesTotal, this.queue);
	}
	function onAllProgress(){
		var frac = (this.bytesUploaded + this.currentBytesUploaded) / this.bytesTotal;
		//$.log('Upload: progress ', Math.round(frac * 100));
		this.options.callbacks.onProgress ? this.options.callbacks.onProgress(this.currentFile, this.currentBytesUploaded / this.currentFile.fileSize, frac) : false;
	}
	function onAllComplete() {
		//$.log('Upload: complete! UPLOADED BYTES: ', this.bytesUploaded);
		this.isRunning = false;
		this.reset();
		this.options.callbacks.onComplete ? this.options.callbacks.onComplete(this.bytesUploaded) : false;
		this.destroy();
	}
	
	
};